
import prodsys
import sys

### This is (more or less) a direct translation of the CLIPS' version of the
### Miss Manners benchmark (manners.clp and manners8.fct) which you can find
### at http://clipsrules.sourceforge.net/. The original source for the OPS5
### benchmark suite (which includes more benchmarks) is available at
### http://www.cs.utexas.edu/ftp/pub/ops5-benchmark-suite/.

## manners8.clp
## ------------

## (deftemplate guest
##   (slot name)
##   (slot sex)
##   (slot hobby))
class Guest(object):
    __slots__ = ['name', 'sex', 'hobby',]
    def __init__(self, name, sex, hobby):
        self.name = name
        self.sex = sex
        self.hobby = hobby
        
    def __repr__(self):
        return "<Guest name = %s, sex = %s, hobby = %s>" % \
               (self.name, self.sex, self.hobby)
               
               
## (deftemplate last_seat
##   (slot seat))
class LastSeat(object):
    __slots__ = ['seat',]
    def __init__(self, seat):
        self.seat = seat
        
    def __repr__(self):
        return "<LastSeat seat = %s>" % (self.seat)
        
        
## (deftemplate seating
##   (slot id)
##   (slot pid)
##   (slot seat1)
##   (slot name1)
##   (slot name2)
##   (slot seat2)
##   (slot path_done))
class Seating(object):
    __slots__ = ['id', 'pid', 'seat1', 'name1', 'name2', 'seat2', 'path_done',]
    def __init__(self, id, pid, seat1, name1, name2, seat2, path_done):
        self.id = id
        self.pid = pid
        self.seat1 = seat1
        self.name1 = name1
        self.name2 = name2
        self.seat2 = seat2
        self.path_done = path_done
        
    def __repr__(self):
        return "<Seating id = %s, pid = %s, seat1 = %s, name1 = %s, name2 = %s, seat2 = %s, path_done = %s>" % \
               (self.id, self.pid, self.seat1, self.name1,
                self.name2, self.seat2, self.path_done)
                
                
## (deftemplate path
##   (slot id)
##   (slot name)
##   (slot seat))
class Path(object):
    __slots__ = ['id', 'name', 'seat',]
    def __init__(self, id, name, seat):
        self.id = id
        self.name = name
        self.seat = seat
        
    def __repr__(self):
        return "<Path id = %s, name = %s, seat = %s>" % \
               (self.id, self.name, self.seat)
               
               
## (deftemplate chosen
##   (slot id)
##   (slot name)
##   (slot hobby))
class Chosen(object):
    __slots__ = ['id', 'name', 'hobby',]
    def __init__(self, id, name, hobby):
        self.id = id
        self.name = name
        self.hobby = hobby
        
    def __repr__(self):
        return "<Chosen id = %s, name = %s, hobby = %s>" % \
               (self.id, self.name, self.hobby)
               
               
## (deftemplate count
##   (slot c))
class Count(object):
    __slots__ = ['c',]
    def __init__(self, c):
        self.c = c
        
    def __repr__(self):
        return "<Count c = %s>" % (self.c)
        
        
## (deftemplate context
##   (slot state))
class Context(object):
    __slots__ = ['state',]
    def __init__(self, state):
        self.state = state
        
    def __repr__(self):
        return "<Context state = %s>" % (self.state)
        
        
## (defrule assign_first_seat
##   ?ctxt <- (context (state start))
##   (guest (name ?n))
##   ?count <- (count (c ?c))
##   =>
##   (assert (seating (seat1 1) (name1 ?n) (name2 ?n) (seat2 1) (id ?c) (pid 0) (path_done yes)))
##   (assert (path (id ?c) (name ?n) (seat 1)))
##   (modify ?count (c (+ ?c 1)))
##   (printout t seat " " 1 " " ?n " " ?n " " 1 " " ?c " " 0 " " 1 crlf)
##   (modify ?ctxt (state assign_seats)))
@prodsys.rule
def assign_first_seat(self, ctxt = Context, count = Count, guest = Guest):
    if ctxt(state == "start") and \
       guest(name == n) and \
       count(c == c_):
            self.assert_fact(Seating(seat1 = 1,
                                     name1 = n,
                                     name2 = n,
                                     seat2 = 1,
                                     id = c_,
                                     pid = 0,
                                     path_done = "yes"))
            self.assert_fact(Path(id = c_,
                                  name = n,
                                  seat = 1))
            self.modify_fact_attr(count, c = c_ + 1)
            print "seat 1", n, n, 1, c_, 0, 1
            self.modify_fact_attr(ctxt, state = "assign_seats")
            
            
## (defrule find_seating
##   ?ctxt <- (context (state assign_seats))
##   (seating (seat1 ?seat1) (seat2 ?seat2) (name2 ?n2) (id ?id) (pid ?pid) (path_done yes))
##   (guest (name ?n2) (sex ?s1) (hobby ?h1))    ; guest1
##   (guest (name ?g2) (sex ~?s1) (hobby ?h1))   ; guest2
##   ?count <- (count (c ?c))
##   (not (path (id ?id) (name ?g2)))
##   (not (chosen (id ?id) (name ?g2) (hobby ?h1)))
##   =>
##   (assert (seating (seat1 ?seat2) (name1 ?n2) (name2 ?g2) (seat2 (+ ?seat2 1)) (id ?c)
##                    (pid ?id) (path_done no)))
##   (assert (path (id ?c) (name ?g2) (seat ( + ?seat2 1))))
##   (assert (chosen (id ?id) (name ?g2) (hobby ?h1)))
##   (modify ?count (c (+ ?c 1)))
##   (printout t seat " " ?seat2 " " ?n2 " " ?g2 crlf)
##   (modify ?ctxt (state make_path)))
@prodsys.rule
def find_seating(self, ctxt = Context, seating = Seating,
                 guest1 = Guest, guest2 = Guest, count = Count,
                 path = Path, chosen = Chosen):
    if ctxt(state == "assign_seats") and \
       seating(seat1 == seat1_, seat2 == seat2_,
               name2 == n2, id == id_, pid == pid_,
               path_done == "yes") and \
       guest1(name == n2, sex == s1, hobby == h1) and \
       guest2(name == g2, sex != s1, hobby == h1) and \
       count(c == c_) and \
       Not(path(id == id__, name == g2)) and \
       Not(chosen(id == id_, name == g2, hobby == h1)):
            self.assert_fact(Seating(seat1 = seat2_, name1 = n2, name2 = g2,
                             seat2 = seat2_ + 1, id = c_,
                             pid = id_, path_done = "no"))
            self.assert_fact(Path(id = c_, name = g2, seat = seat2_ + 1))
            self.modify_fact_attr(count, c = c_ + 1)
            print "seat", seat2_, n2, g2
            self.modify_fact_attr(ctxt, state = "make_path")
            
            
## (defrule path_done
##   ?ctxt <- (context (state make_path))
##   ?seat <- (seating (path_done no))
##   =>
##   (modify ?seat (path_done yes))
##   (modify ?ctxt (state check_done)))
@prodsys.rule
def path_done(self, ctxt = Context, seat = Seating):
    if ctxt(state == "make_path") and \
       seat(path_done == "no"):
            self.modify_fact_attr(seat, path_done = "yes")
            self.modify_fact_attr(ctxt, state = "check_done")
            
            
## (defrule make_path
##   (context (state make_path))*
##   (seating (id ?id) (pid ?pid)* (path_done no))
##   (path (id ?pid)* (name ?n1) (seat ?s))      ; path1
##   (not (path (id ?id) (name ?n1)))            ; path2
##   =>
##   (assert (path (id ?id) (name ?n1) (seat ?s))))
@prodsys.rule
def make_path(self, context = Context, seating = Seating, path1 = Path, path2 = Path):
    if context(state == "make_path") and \
       seating(id == id_, pid == pid_, path_done == "no") and \
       path(id == pid_, name == n1, seat == s) and \
       Not(path(id == id_, name == n1)):
            self.assert_fact(Path(id = id_, name = n1, seat = s))
            
            
## (defrule continue
##   ?ctxt <- (context (state check_done))
##   =>
##   (modify ?ctxt (state assign_seats)))
@prodsys.rule
def continue_(self, ctxt = Context):
    if ctxt(state == "check_done"):
            self.modify_fact_attr(ctxt, state = "assign_seats")
            
            
## (defrule are_we_done
##   ?ctxt <- (context (state check_done))
##   (last_seat (seat ?l_seat))
##   (seating (seat2 ?l_seat))
##   =>
##   (printout t crlf "Yes, we are done!!" crlf)
##   (modify ?ctxt (state print_results)))
@prodsys.rule
def are_we_done(self, ctxt = Context, last_seat = LastSeat, seating = Seating):
    if ctxt(state == "check_done") and \
       last_seat(seat == l_seat) and \
       seating(seat2 == l_seat):
            print "\nYes, we are done!!"
            self.modify_fact_attr(ctxt, state = "print_results")
            
            
## (defrule print_results
##   (context (state print_results))
##   (seating (id ?id) (seat2 ?s2))
##   (last_seat (seat ?s2))
##   ?path <- (path (id ?id) (name ?n) (seat ?s))
##   =>
##   (retract ?path)
##   (printout t ?n " " ?s crlf))
@prodsys.rule
def print_results(context = Context, seating = Seating, last_seat = LastSeat, path = Path):
    if context(state == "print_results") and \
       seating(id == id_, seat2 == s2) and \
       last_seat(seat == s2) and \
       path(id == id_, name == n, seat == s):
            self.retract_fact(path)
            print n, s
            
            
## (defrule all_done
##   (context (state print_results))
##   =>
##   (halt))
@prodsys.rule
def all_done(context = Context):
    if context(state == "print_results"):
            sys.exit()
            
            
## manners8.fct
## ------------

## (guest (name n1) (sex m) (hobby h3))
## (guest (name n1) (sex m) (hobby h2))
g011 = Guest(name = "n1", sex = "M", hobby = "h3")
g012 = Guest(name = "n1", sex = "M", hobby = "h2")

## (guest (name n2) (sex m) (hobby h2))
## (guest (name n2) (sex m) (hobby h3))
g021 = Guest(name = "n2", sex = "M", hobby = "h2")
g022 = Guest(name = "n2", sex = "M", hobby = "h3")

## (guest (name n3) (sex m) (hobby h1))
## (guest (name n3) (sex m) (hobby h2))
## (guest (name n3) (sex m) (hobby h3))
g031 = Guest(name = "n3", sex = "M", hobby = "h1")
g032 = Guest(name = "n3", sex = "M", hobby = "h2")
g033 = Guest(name = "n3", sex = "M", hobby = "h3")

## (guest (name n4) (sex f) (hobby h3))
## (guest (name n4) (sex f) (hobby h2))
g041 = Guest(name = "n4", sex = "F", hobby = "h3")
g042 = Guest(name = "n4", sex = "F", hobby = "h2")

## (guest (name n5) (sex f) (hobby h1))
## (guest (name n5) (sex f) (hobby h2))
## (guest (name n5) (sex f) (hobby h3))
g051 = Guest(name = "n5", sex = "F", hobby = "h1")
g052 = Guest(name = "n5", sex = "F", hobby = "h2")
g053 = Guest(name = "n5", sex = "F", hobby = "h3")

## (guest (name n6) (sex f) (hobby h3))
## (guest (name n6) (sex f) (hobby h1))
## (guest (name n6) (sex f) (hobby h2))
g061 = Guest(name = "n6", sex = "F", hobby = "h3")
g062 = Guest(name = "n6", sex = "F", hobby = "h1")
g063 = Guest(name = "n6", sex = "F", hobby = "h2")

## (guest (name n7) (sex f) (hobby h3))
## (guest (name n7) (sex f) (hobby h2))
g071 = Guest(name = "n7", sex = "F", hobby = "h3")
g072 = Guest(name = "n7", sex = "F", hobby = "h2")

## (guest (name n8) (sex m) (hobby h3))
## (guest (name n8) (sex m) (hobby h1))
g081 = Guest(name = "n8", sex = "M", hobby = "h3")
g082 = Guest(name = "n8", sex = "M", hobby = "h1")

guests = [g011,g012,
          g021,g022,
          g031,g032,g033,
          g041,g042, 
          g051,g052,g053,
          g061,g062,g063,
          g071,g072,
          g081,g082,]
          
## (last_seat (seat 8))
ls = LastSeat(seat = 8)

## (count (c 1))
count = Count(c = 1)

## (context (state start))
context = Context(state = "start")

ruleengine = prodsys.ruleengine(locals())
ruleengine.watch(   ACTIVATIONS, STATISTICS, FACTS  )

ruleengine.add_rule(assign_first_seat,
                    find_seating,
                    path_done,
                    make_path,
                    continue_,
                    are_we_done,
                    print_results,
                    all_done)
ruleengine.assert_fact(ls, count, context, deffact = True)
ruleengine.assert_fact(guests, deffact = True)

ruleengine.reset()
ruleengine.run()
